package mysgoog.bot;

import java.util.*;

import mysgoog.MC.MCManager;
import mysgoog.board.*;
import mysgoog.bot.algo.*;
import mysgoog.brown.*;
import mysgoog.gtp.*;
import mysgoog.pattern.*;
import mysgoog.pattern.Storage.VertexValue;
import mysgoog.util.Lib;

public class CombinedBot extends GoBot {
	static final private int SafeThres = 10;
	private DragonBoard board;
	private PatternMatcher ptnMatcher;
	{
		try {
			ptnMatcher = new PatternMatcher();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void setBoard(Board board) {
		this.board = (DragonBoard) board;
	}

	@Override
	public Vertex genMove(Color color) {
		if (board.getTurn() <= 10) {
			Vertex open = openning(color);
			if (open != null)
				return open;
		}
		// nears
		if (board.getHistory().size() != 0) {
			int lastMove = board.getHistory().getLast().pos;
			HashSet<Dragon> nearDragons = new HashSet<Dragon>();
			for (int pos : board.getAround(lastMove, 1)) {
				Dragon dragon = board.dragonAt(pos);
				if (dragon != null && dragon.color == color)
					nearDragons.add(dragon);
			}
			ArrayList<Dragon> list = new ArrayList<Dragon>();
			for (Dragon dragon : nearDragons)
				if (dragon.isDangerous())
					list.add(dragon);
			// someone is in danger, save it
			if (list.size() > 0) {
				Comparator<Dragon> cmp = new Comparator<Dragon>() {
					@Override
					public int compare(Dragon o1, Dragon o2) {
						return -(o1.size() - o2.size());
					}
				};
				Collections.sort(list, cmp);
				for (Dragon dragon : list) {
					HashMap<Integer, ValuedPos> map = new HashMap<Integer, ValuedPos>();
					for (Worm worm : dragon) {
						int[] res = new int[board.BOARD_ELEMNUM];
						int len = AlphaBetaSearch.Defend(board, worm.getOne(),
								res);
						if (len > 0)
							for (int i = 0; i < len; i++) {
								int pos = res[i];
								if (!board.isLegal(color, pos)
										|| !checkEyes(color, pos)) {
									continue;
									// TODO throw new Error("!!!");
								}
								if (map.get(pos) == null)
									map.put(pos, new ValuedPos(pos, 0));
								map.get(pos).val += SafeThres
										- worm.getLibs().size();
							}
					}
					ValuedPos best = null;
					for (ValuedPos vp : map.values()) {
						if (best == null || vp.val > best.val)
							best = vp;
					}
					if (best != null) {
						Lib.debug("[def]");
						return new Vertex(best.pos);
					}
				}
			}
		}
		// atk
		ArrayList<Dragon> list = new ArrayList<Dragon>();
		for (Dragon dragon : board.getDragons(color.complement()))
			if (dragon.isDangerous())
				list.add(dragon);
		// try ko
		if (list.size() > 0) {
			Comparator<Dragon> cmp = new Comparator<Dragon>() {
				@Override
				public int compare(Dragon a, Dragon b) {
					int res = -(a.getLibs().size() - b.getLibs().size());
					if (res == 0)
						res = -(a.size() - b.size());
					return res;
				}
			};
			Collections.sort(list, cmp);
			for (Dragon dragon : list) {
				HashMap<Integer, ValuedPos> map = new HashMap<Integer, ValuedPos>();
				for (Worm worm : dragon) {
					int[] res = new int[board.BOARD_ELEMNUM];
					int len = AlphaBetaSearch
							.Capture(board, worm.getOne(), res);
					if (len > 0)
						for (int i = 0; i < len; i++) {
							int pos = res[i];
							if (!board.isLegal(color, pos)
									&& !checkAllEyes(color, pos)) {
								continue;
								// TODO throw new Error("!!!");
							}
							if (map.get(pos) == null)
								map.put(pos, new ValuedPos(pos, 0));
							map.get(pos).val += SafeThres
									- worm.getLibs().size();
						}
				}
				ValuedPos best = null;
				for (ValuedPos vp : map.values()) {
					/*
					 * board.play(color, vp.pos); vp.val *=
					 * board.dragonAt(vp.pos).getPON(); board.undo();
					 */
					if (best == null || vp.val > best.val)
						best = vp;
				}
				if (best != null) {
					Lib.debug("[atk]");
					return new Vertex(best.pos);
				}
			}
		}
		// pattern
		List<VertexValue> pattern = this.ptnMatcher.getSmallPatternMatch(board,
				color);
		if (pattern.size() > 0)
			for (VertexValue vv : pattern) {
				int pos = board.makePos(vv.vertex.x, vv.vertex.y);
				if (board.isLegal(color, pos) && !board.isEdge(pos)) {
					Lib.debug("[pattern]");
					return vv.vertex;
				}
			}
		// explore empty area
		ArrayList<Integer> rndList = new ArrayList<Integer>();
		for (int pos = 0; pos < board.BOARD_ELEMNUM; pos++)
			if (!board.isEdge(pos) && !board.getStone(pos).isFilled()) {
				boolean ok = true;
				for (int a : board.getAround(pos))
					if (board.getStone(a).isFilled()) {
						ok = false;
						break;
					}
				if (ok)
					rndList.add(pos);
			}
		if (rndList.size() > 0) {
			int pos = rndList.get(Lib.random(rndList.size()));
			if (!board.isLegal(color, pos))
				throw new Error("!!!");
			Lib.debug("[explore]");
			return new Vertex(pos);
		}
		MCManager mcmanager = new MCManager(color, this.board);
		Vertex ret = mcmanager.MCGen();
		if (board.isLegal(color, board.makePos(ret.x, ret.y))) {
			int pos = board.makePos(ret.x, ret.y);
			if (checkAllEyes(color, pos)) {
				Lib.debug("[MC]");
				return ret;
			}
		}
		// random
		rndList.clear();
		for (int pos = 0; pos <= board.BOARD_ELEMNUM; pos++)
			if (board.isLegal(color, pos) && checkAllEyes(color, pos)) {
				rndList.add(pos);
			}
		if (rndList.size() > 0) {
			int pos = rndList.get(Lib.random(rndList.size()));
			if (!board.isLegal(color, pos))
				throw new Error("!!!");
			Lib.debug("[random]");
			return new Vertex(pos);
		}
		return new Vertex("pass");
	}

	private Vertex openning(Color color) {
		int star = checkStars();
		if (star != -1) {
			return new Vertex(board.posX(star), board.posY(star));
		}
		List<VertexValue> list = ptnMatcher.getMatch(board, color);
		if (list.size() > 0) {
			for (VertexValue vv : list)
				if (board.isLegal(color,
						board.makePos(vv.vertex.x, vv.vertex.y)))
					return vv.vertex;
		}
		return null;
	}

	private int checkStars() {
		if (board.getTurn() >= 4)
			return -1;
		for (int pos : board.getStars())
			if (board.territoryAt(pos) == null)
				return pos;
		return -1;
	}

	private boolean checkEyes(Color color, int pos) {
		Dragon dragon = board.territoryAt(pos);
		if (dragon == null || dragon.color != color)
			return true;
		if (dragon.getEyes().contains(pos))
			return false;
		else
			return true;
	}

	private boolean checkAllEyes(Color color, int pos) {
		Dragon dragon = board.territoryAt(pos);
		if (dragon == null || dragon.color != color)
			return true;
		if (dragon.getEyes().contains(pos)
				|| dragon.getHalfEyes().contains(pos))
			return false;
		else
			return true;
	}
}
